﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace ColiyerLogger
{
    internal class LogImpl : ILog
    {
        private const int EMTY_TIMS_LIMIT = 10;
        /// <summary>
        /// 消息缓存
        /// </summary>
        private static ConcurrentBag<_LogInnerMessage> _msgBak = new ConcurrentBag<_LogInnerMessage>();
        /// <summary>
        /// 消费运行标志
        /// </summary>
        private static bool _isConsumerRuning = false;
        /// <summary>
        /// 同步锁
        /// </summary>
        private static readonly object _asyncLock=new object();
        /// <summary>
        /// 消费线程
        /// </summary>
        private static Task _consumerTask;
        private static string _rootDirectory =Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"log");
        public string RootDirectory { get => _rootDirectory; set {
                _rootDirectory = value;
            }
        }
        private enum _LogType
        {
            /// <summary>
            /// 错误
            /// </summary>
            ERROR,
            /// <summary>
            /// 信息
            /// </summary>
            TEST,
            /// <summary>
            /// 运行信息
            /// </summary>
            RUN,
        }
        private class _LogInnerMessage
        {
            private readonly string _message;
            private readonly _LogType _type;
            private DateTime _created;

            public _LogInnerMessage(string message, _LogType type)
            {
                _message = message;
                _type = type;
                _created = DateTime.Now;
            }

            public string Message => _message;

            public _LogType Type => _type;

            public DateTime Created => _created;
        }
        private void Push(_LogInnerMessage message)
        {
            if (message ==null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            _msgBak.Add(message);
            StartConsumer();
        }

        private void StartConsumer()
        {
            lock (_asyncLock)
            {
                if (!_isConsumerRuning)
                {
                    _isConsumerRuning=true;
                    _consumerTask = Task.Run(()=>ConsumeMessages());
                }
            }
        }

        private void  ConsumeMessages()
        {
            int emtyTims = 0;
            while (true)
            {
                if (_msgBak.IsEmpty)
                {
                   Thread.Sleep(100);
                    if (emtyTims++>EMTY_TIMS_LIMIT)
                    {
                        lock (_asyncLock)
                        {
                            _isConsumerRuning = false;
                            break;
                        }
                    }
                    else
                    {
                        continue;
                    }
                }
                if (_msgBak.TryTake(out var msg))
                {
                    try
                    {
                        StoreMessageLocally(msg);
                        emtyTims = 0;
                    }
                    catch (Exception ex)
                    {
                        emtyTims++;
                        LogError(ex);
                    }
                }
            }
        }

        private void StoreMessageLocally(_LogInnerMessage msg)
        {
            string directoryPath = BuildDreictoryPath(msg);

            // 确保目录存在
            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }

            // 构建日志文件路径，将 msg.Type 作为文件名的一部分
            string logFilePath = Path.Combine(directoryPath, $"{msg.Created:yyyy_MM_dd}_{msg.Type}.log");

            // 打开文件，若文件不存在则创建，若存在则追加内容
            using (FileStream fs = new FileStream(logFilePath, FileMode.Append, FileAccess.Write))
            {
                using (StreamWriter sw = new StreamWriter(fs))
                {
                    StringBuilder builder = new StringBuilder();
                    builder.Append($"{msg.Created:yyyy-MM-dd HH:mm:ss} - ");
                    builder.Append(msg.Message);

                    // 写入日志行
                    sw.WriteLine(builder.ToString());
                }
            }
        }

        private string BuildDreictoryPath(_LogInnerMessage msg)
        {
            string drecitoryPath = $"{msg.Type}";
            return Path.Combine(_rootDirectory,drecitoryPath);
        }
        private static string DataSerialzer<TData>(TData data)
        {
            string jsonString = JsonSerializer.Serialize(data);
            return jsonString;
        }

        public void LogError(Exception ex)
        {
            Push(new _LogInnerMessage(ex.ToString(),_LogType.ERROR));
        }

        public void LogErrorWithData<TData>(Exception ex, string tip, TData data)
        {
            string serializedData = DataSerialzer(data);
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.AppendLine($"Exception: {ex}");
            messageBuilder.AppendLine($"Tip: {tip}");
            messageBuilder.AppendLine($"Data: {serializedData}");

            Push(new _LogInnerMessage(messageBuilder.ToString(), _LogType.ERROR));
        }

        public void LogErrorWithDatas<TData>(Exception ex, string tip, IEnumerable<TData> data)
        {
            string serializedData = DataSerialzer(data);
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.AppendLine($"Exception: {ex}");
            messageBuilder.AppendLine($"Tip: {tip}");
            messageBuilder.AppendLine($"Data List: {serializedData}");

            Push(new _LogInnerMessage(messageBuilder.ToString(), _LogType.ERROR));
        }

        public void LogTest(string tip)
        {
            Push(new _LogInnerMessage(tip,_LogType.TEST));
        }

        public void LogTestWithData<TData>(string tip, TData data)
        {
            string serializedData = DataSerialzer(data);
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.AppendLine($"Tip: {tip}");
            messageBuilder.AppendLine($"Data: {serializedData}");

            Push(new _LogInnerMessage(messageBuilder.ToString(), _LogType.TEST));
        }

        public void LogTestWithDatas<TData>(string tip, IEnumerable<TData> data)
        {
            string serializedData = DataSerialzer(data);
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.AppendLine($"Tip: {tip}");
            messageBuilder.AppendLine($"Data List: {serializedData}");

            Push(new _LogInnerMessage(messageBuilder.ToString(), _LogType.TEST));
        }

        public void LogRun(string tip)
        {
            Push(new _LogInnerMessage(tip,_LogType.RUN));
        }
    }
}
